using System;
using System.Linq;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics.Contracts;
using FoundationLib.Ifc.Rules;


namespace FoundationLib.Ifc.Rules 
{
	/// <summary>
	/// Abstract superclass for a business rule
	/// </summary>
	public abstract class RuleBase : IRule 
	{
		/// <summary>
		/// The name of the business rule category
		/// </summary>
		private string category;
		/// <summary>
		/// The business rule identifier
		/// </summary>
		private int id;
		/// <summary>
		/// A list of logical fields or properties involved in this business rule
		/// </summary>
		private List<string> logicalFieldsInvolved;
		/// <summary>
		/// The name of the resource containing a message indicating invalidity
		/// </summary>
		private string messageResourceName;

		/// <summary>
		/// Creates a new business rule
		/// </summary>
		protected RuleBase()
		{
			this.logicalFieldsInvolved = new List<string>();
		}

		/// <summary>
		/// Creates a new business rule
		/// </summary>
		/// <param name="id">The business rule ID</param>
		/// <param name="messageResourceName">The name of the resource containing a message
		/// indicating entity invalidity</param>
		protected RuleBase(int id, string messageResourceName)
			: this()
		{
			this.id = id;
			this.messageResourceName = messageResourceName;
		}

		/// <summary>
		/// Creates a new business rule
		/// </summary>
		/// <param name="id">The business rule ID</param>
		/// <param name="messageResourceName">The name of the resource containing a message
		/// indicating entity invalidity</param>
		/// <param name="category">The name of the business rule category</param>
		protected RuleBase(int id, string messageResourceName, string category)
			: this(id, messageResourceName)
		{
			this.category = category;
		}

		/// <summary>
		/// Adds a field or property involved in this business rule
		/// </summary>
		/// <param name="field">The name of the field or property</param>
		public void AddLogicalFieldInvolved(string field)
		{
			if (!this.logicalFieldsInvolved.Any(x => x.Equals(field, StringComparison.CurrentCultureIgnoreCase)))
				this.logicalFieldsInvolved.Add(field);
		}

		/// <summary>
		/// Gets a name indicating the business rule category
		/// </summary>
		public string Category
		{
			get
			{
				return this.category;
			}
		}

		/// <summary>
		/// Removes all fields or properties involved in this business rule
		/// </summary>
		public void ClearLogicalFieldsInvolved()
		{
			this.logicalFieldsInvolved.Clear();
		}

		/// <summary>
		/// Collects all broken rules from a given set of business rules
		/// </summary>
		/// <returns>The number of broken business rules collected</returns>
		/// <exception cref="ArgumentNullException">One of the given parameters contains a null-
		/// reference</exception>
		/// <param name="result">The result collection populated with broken rules</param>
		/// <param name="rules">The set of rules to be validated</param>
		public static int CollectBrokenRules(IList<IRule> result, IEnumerable<IRule> rules)
		{
			if (result == null)
				throw new ArgumentNullException("result");
			if (rules == null)
				throw new ArgumentNullException("rules");

			IEnumerable<IRule> brokenRules = rules.Where(x => x != null && !x.IsValid);
			foreach (IRule item in brokenRules)
			{
				result.Add(item);
			}

			return brokenRules.Count();
		}

		/// <summary>
		/// Gets the unique business rule ID
		/// </summary>
		public int Id
		{
			get
			{
				return this.id;
			}
		}

		/// <summary>
		/// Gets a value indicating whether the entity is valid regarding this business
		/// rule
		/// </summary>
		public abstract bool IsValid
		{
			get;
		}

		/// <summary>
		/// Gets an array containing the logical fields or properties involved in this
		/// business rule
		/// </summary>
		public string[] LogicalFieldsInvolved
		{
			get
			{
				return this.logicalFieldsInvolved.ToArray();
			}
		}

		/// <summary>
		/// Gets the name of the resource containing a message indicating rule invalidity
		/// </summary>
		public string MessageResourceName
		{
			get
			{
				return this.messageResourceName;
			}
		}

		/// <summary>
		/// Removes a field or property involved in this business rule
		/// </summary>
		/// <returns>True if the field was successfully removed, false else</returns>
		/// <param name="field">The name of the field or property</param>
		public bool RemoveLogicalFieldInvolved(string field)
		{
			return (this.logicalFieldsInvolved.Remove(field));
		}

	}

}